package com.xuantian.baoming.utils;

import lombok.SneakyThrows;

import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;

/**
 * 非对称算法加解密工具类 一般支持 RSA\DSA\DiffieHellman
 *
 * RasKeyPair pair = generatorKeyPair(1024);
 * String secretText = encrypt("abc", pair.toPublicKey());
 * System.out.println(secretText);  //drkmfcUbDyv5Szb8qjYeSVZ2vWHcCfQ1ZI1rIQKeieYBXJ4VnrSTaCiGwcdIPD7HQZQW84REmADKqlWrvPet1FG26jlotTZApssD1EdLioWDIC+SF39zHpnQEHyS7zIp/dGnI45GfW1e82ouUZfKRsrktzS/J1Rw/2yeHAEGiAE=
 * String original = decrypt(secretText, pair.toPrivateKey());
 * System.out.println(original);    //abc
 *
 * @link <a href="https://blog.csdn.net/bestcxx/article/details/99933509/">非对称算法加解密工具类</a>
 * @author feizns
 * @since 2019/6/25
 */
public class Rsa {

    /**
     * RSA加解密器
     * <pre>{@code
     * //获取加解密算法
     * EncryptionAlgorithm rsa = EncryptionAlgorithm.RSA;
     * //生成公私钥
     * KeyPair pair = rsa.generatorKeyPair(1024);
     * //使用公钥对字符进行加密
     * String secretText = rsa.encrypt("abc", pair.toPublicKey());
     * //加密后的字符
     * System.out.println(secretText);
     * //对加密后的字符使用私钥解密
     * String original = rsa.decrypt(secretText, pair.toPrivateKey());
     * //解密后的原字符
     * System.out.println(original);    //abc
     * }</pre>
     */
    public static final Rsa INSTANCE = new Rsa("RSA");

    /**
     * RSA加密算法
     * @return {@link Rsa}
     */
    public static Rsa instance() {
        return INSTANCE;
    }

    /**
     * 非对称算法加解密方法一般支持 RSA\DSA\DiffieHellman
     */
    private final String method;

    /**
     * 非对称算法加解密方法一般支持 RSA\DSA\DiffieHellman
     * @param method RSA|DSA|DiffieHellman
     */
    public Rsa(String method) {
        this.method = method;
    }

    /**
     * 将字符使用对应的公钥加密
     * @param str 字符
     * @param key {@link PublicKey}
     * @return 加密后的字符串（该字符可以使用生成公钥时、所使用的私钥解密还原）
     */
    @SneakyThrows
    public String encrypt(String str, PublicKey key) {
        Cipher cipher = Cipher.getInstance(method);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes()));
    }

    /**
     * 将字符使用对应的公钥加密
     * @param str 字符
     * @param base64PublicKey {@link String}
     * @return 加密后的字符串（该字符可以使用生成公钥时、所使用的私钥解密还原）
     */
    @SneakyThrows
    public String encrypt(String str, String base64PublicKey) {
        return encrypt(str, getPublicKey(base64PublicKey));
    }

    /**
     * 将字符使用对应的私钥解密
     * @param str 字符
     * @param key {@link PrivateKey}
     * @return 解密后的字符
     */
    public String decrypt(String str, PrivateKey key) {
        try {
            byte[] bytes = Base64.getDecoder().decode(str.getBytes());
            Cipher cipher;
            cipher = Cipher.getInstance(method);
            cipher.init(Cipher.DECRYPT_MODE, key);
            return new String(cipher.doFinal(bytes));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将字符使用对应的私钥解密
     * @param str 字符
     * @param base64PrivateKey {@link String}
     * @return 解密后的字符
     */
    public String decrypt(String str, String base64PrivateKey) {
        return decrypt(str, getPrivateKey(base64PrivateKey));
    }

    /**
     * 生成指定位数的公私钥键值对
     * @link <a href="https://blog.csdn.net/bestcxx/article/details/99933509/">非对称算法加解密工具类</a>
     * @param size 字符位数（常用有：1024,2048）
     * @return {@link KeyPair}
     */
    @SneakyThrows
    public KeyPair generatorKeyPair(int size) {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(method);
        SecureRandom secureRandom = new SecureRandom(new Date().toString().getBytes());
        keyPairGenerator.initialize(size, secureRandom);
        java.security.KeyPair keyPair = keyPairGenerator.genKeyPair();
        return new KeyPair(keyPair);
    }

    /**
     * 将base64位的字符公钥转换为{@link PublicKey}
     * @see PublicKey
     * @see #getPublicKey(byte[])
     * @param base64PublicKey base64公钥
     * @return {@link PublicKey}
     */
    public PublicKey getPublicKey(String base64PublicKey) {
        return getPublicKey(EncryptUtils.base64Decode(base64PublicKey.getBytes()));
    }

    /**
     * 将base64位的字符私钥转换为{@link PrivateKey}
     * @see PrivateKey
     * @see #getPrivateKey(byte[])
     * @param base64PrivateKey base64私钥
     * @return {@link PrivateKey}
     */
    public PrivateKey getPrivateKey(String base64PrivateKey) {
        return getPrivateKey(EncryptUtils.base64Decode(base64PrivateKey.getBytes()));
    }

    /**
     * 将字节内容转换为{@link PublicKey}
     * @see PublicKey
     * @param bytes 字节数据
     * @return {@link PublicKey}
     */
    @SneakyThrows
    public PublicKey getPublicKey(byte[] bytes) {
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory kf = KeyFactory.getInstance(method);
        return kf.generatePublic(spec);
    }

    /**
     * 将字节内容转换为{@link PrivateKey}
     * @see PrivateKey
     * @param bytes 字节数据
     * @return {@link PrivateKey}
     */
    @SneakyThrows
    public PrivateKey getPrivateKey(byte[] bytes) {
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory kf = KeyFactory.getInstance(method);
        return kf.generatePrivate(spec);
    }

}
